home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Simtel MSDOS 1995 January
/
Simtel - 10000 MSDOS Shareware Programs (Walnut Creek)(January 1995)(Disc 2).ISO
/
disc2
/
eel
/
cxtnd.e
< prev
next >
Wrap
Text File
|
1989-02-09
|
25KB
|
776 lines
/************************************************************************
* *
* C-Extended Mode ver 1.0 *
* *
* ADDITIONS AND ENHANCEMENTS--03/24/87 *
* *
* Author: John Kubiatowicz *
* KUBITRONICS information systems *
* (C) March 24, 1987 *
* kubitron%athena.mit.edu *
* *
* Much of this material is (c) 1987, by John Kubiatowicz. *
* The remainder is (c) 1985, Lagaru Software Ltd. *
* *
* This material may be used and distributed freely, baring *
* use in products for sale or lease. The author makes no *
* claims of perfection, and will not be responsible for *
* damages incurred during the use of this product. *
* *
************************************************************************/
/************************************************************************
* "Epsilon", "EEL" and "Lugaru" are trademarks of Lugaru Software, Ltd. *
* *
* Copyright (C) 1985 Lugaru Software Ltd. All rights reserved. *
* *
* Limited permission is hereby granted to reproduce and modify this *
* copyrighted material provided that the resulting code is used only in *
* conjunction with Lugaru products and that this notice is retained in *
* any such reproduction or modification. *
************************************************************************/
#include "eel.h"
/* Original Lagaru Comments
Automatic indentation for C and EEL code. Several indenting styles
are supported, selected by the following variables.
* * * *
The position of the closing brace is controlled by the Closeback
variable, 1 by default:
Closeback = 0; Closeback = 1;
if (foo) { if (foo) {
bar(); bar();
baz(); baz();
} }
By placing the opening brace on the following line, these styles
may be also used:
Closeback = 0; Closeback = 1;
if (foo) if (foo)
{ {
bar(); bar();
baz(); baz();
} }
* * * *
The Topindent variable controls whether top-level statements in a function
are indented. It is 1 by default.
Topindent = 0; Topindent = 1;
foo() foo()
{ {
if (bar) if (bar)
baz(); baz();
} }
* * * *
The Matchdelim variable controls whether typing ), ], or } displays the
corresponding (, [, or { when the former is typed. It uses the
show-matching-delimiter command.
*/
/* Comments on C-Extended Mode
John Kubiatowicz
In this file, I have implemented 'switch' statement indentation.
This feature is selected by setting the flag 'Switch_form', which
appears below. The following switch format is supported:
Closeback = 0; Closeback = 1;
switch(foo) { switch(foo) {
case 0: case 0:
statement1; statement1;
statement2; statement2;
case 1: case 1;
case 2: case 2;
statement3; statement3;
statement4; statement4;
} }
In addition, I have altered the c_compute_indent() so that if
the point is inside of a comment sequence, indentation
will match that of the previous, non-blank line. If the
initial character on the line is a '}' and Match_delim is set,
then tabbing is adjusted as in c_close, which includes showing
the matching delimiter.
I have altered the c_close routine so that closing braces are aligned
"properly" with opening braces. Further, the routine
'show_matching_delimiter' has been replaced with 'c_look_for_matching'.
During an attempt to match a delimiter from outside of quotes,
delimiters within single quotes are ignored, as are delimiters within
double quotes. During an attempt to match a delimiter from inside
quotes, only delimiters within the same string are considered.
Ten extended marks have been added, bound to the Alt-F[1-10]
and the Ctrl-F[1-10] characters. CTRL-F[1-10] sets a mark at the
current location. Ctrl-F[1-10] goes to the preset location.
During such a move, the last position is saved and can be recalled
with Alt-ESC. Bindings for these functions can be changed, although
the ten set keys must have consecutive bindings, as must the ten
recall keys. See the macros GET_MARK_BASE, SET_MARK_BASE,
and C_RETRIEVE_MARK.
For those of you who don't place frequent comments in your code,
a slight speed improvement in delimiter matching can be accomplished
by setting the flag 'Comment_check_enable' to zero. Comments will no
longer be recognized.
The tab size automatically defaults to 4 for all .c,.h,.e, and .y
files.
Finally, I have included the "reload file" function, which reloads
the current file off the disk, discarding the current buffer. This
is bound to 'C-X C-R'. Note that this command is modelled afted a
similar command by Bob Knight.
I have other ideas for this code, like a new 'c_compute_indent',
which I would like to implement at some later time. Perhaps I
will work on version 1.5.....
Good Luck,
KUBI
kubitron%athena.mit.edu
P.S. It is quite possible that this code still has some bugs in it.
I would appreciate any comments or suggestions.
P.P.S. I have removed all tabs from this file. The file was originally
composed with a tab size of 4 (which is the default in c_mode). You
should probably decide on a tab size, then perform a 'tabify-region'
to save space.
*****************************************************************/
/* Base for get_extended_mark and set_extended_mark keys. */
#define GET_MARK_BASE FALT(1)
#define SET_MARK_BASE FCTRL(1)
#define C_RETRIEVE_MARK ALT(ESC)
/* Special global format flags */
int Switch_form = 1; /* Switch statement flag, set to on */
int Comment_check_enable = 1;
/* C-EXT mode globals */
int Match_position = -1; /* Indicates position of last matched delimiter.*/
int quote_value = 0; /* Indicates value of last quote match */
int quote_position = -1; /* Indicates position of last quote */
int comment_value = 0; /* Indicates value of last comment match */
int comment_position = -1; /* Indicates position of beginning of
last comment match. */
/* C-EXT mode buffer vars */
buffer int *extended_mark[10] = {0,0,0,0,0,0,0,0,0,0};
/* Ten extended marks (spots start at NULL) */
buffer int *prev_ext_mark = 0; /* Previous extended mark */
keytable c_tab; /* key table for c mode */
/* by default, the indenting levels are one tab stop apart */
#define INCR tab_size
/* define an RE matching C comments or whitespace */
#define C_SKIP "((/%*([^*]|[^/]%*)*%*/)|[ \t\n])*"
c_indenter() /* replace surrounding indentation with new */
{
to_column(c_compute_indent());
}
command c_look_for_matching();
command c_set_extended_mark();
command c_get_extended_mark();
command c_retrieve_mark();
#define SMB(c) (SET_MARK_BASE + (c))
#define GMB(c) (GET_MARK_BASE + (c))
command c_mode()
{
mode_keys = c_tab; /* use these keys */
if (Matchdelim)
c_tab[')'] = c_tab[']'] = (short) c_look_for_matching;
c_tab[SMB(0)] = c_tab[SMB(1)] = (short) c_set_extended_mark;
c_tab[SMB(2)] = c_tab[SMB(3)] = (short) c_set_extended_mark;
c_tab[SMB(4)] = c_tab[SMB(5)] = (short) c_set_extended_mark;
c_tab[SMB(6)] = c_tab[SMB(7)] = (short) c_set_extended_mark;
c_tab[SMB(8)] = c_tab[SMB(9)] = (short) c_set_extended_mark;
c_tab[GMB(0)] = c_tab[GMB(1)] = (short) c_get_extended_mark;
c_tab[GMB(2)] = c_tab[GMB(3)] = (short) c_get_extended_mark;
c_tab[GMB(4)] = c_tab[GMB(5)] = (short) c_get_extended_mark;
c_tab[GMB(6)] = c_tab[GMB(7)] = (short) c_get_extended_mark;
c_tab[GMB(8)] = c_tab[GMB(9)] = (short) c_get_extended_mark;
c_tab[C_RETRIEVE_MARK] = (short) c_retrieve_mark;
major_mode = strsave("C-Extended");
make_mode();
indenter = c_indenter;
auto_indent = 1;
tab_size = 4;
}
/* make this the default mode for .c, .h, .e, and .y files */
suffix_c() { c_mode(); }
suffix_h() { c_mode(); }
suffix_e() { c_mode(); }
suffix_y() { c_mode(); }
/* Look at the last two lines and return the correct indentation
for the current line, assuming C source (or similar). At start,
we must be at the end of a line's indentation.
*/
c_compute_indent()
{
int orig = point, prev_keyword;
int prev_end = ';', prev2_end = ';'; /* last chr on 2 prev lines */
int ind = 0; /* indentation to use */
int first_char; /* cur line's first char */
/* If within a comment, match indentation to previous line */
if (in_comment_check()) {
re_search(-1, "[ \t\n]*");
to_indentation();
ind = current_column(); /* Current column should match previous */
point = orig;
return ind;
}
first_char = curchar();
if (first_char == '}' && Matchdelim) {
point++;
c_close_compute();
point--;
return current_column();
}
to_begin_line();
re_search(-1, C_SKIP); /* skip whitespace and comments */
if (point > 0) {
prev_end = character(--point); /* get last relevant char */
to_indentation();
ind = current_column(); /* and indentation of its line */
prev_keyword = point; /* save its position too */
re_search(-1, C_SKIP); /* skip whitespace and comments */
if (point > 0) /* and get last interesting char on */
prev2_end = character(point-1); /* line before that */
point = prev_keyword; /* prepare for checking prev line */
}
if (!ind) { /* special case if no previous indentation */
if (top_indent(prev_end, prev2_end))
ind += INCR;
} else if (prev_end == ';') {
if (!index(";{}", prev2_end) && (!Switch_form || prev2_end != ':'))
ind -= INCR;
} else if (prev_end == '}') {
if (!Closeback)
ind -= INCR;
} else if (prev_end != '{' || index(";{}", prev2_end)
|| c_statement_start() || just_open())
ind += INCR;
/* otherwise, last is probably continuation of a stmt or func decl */
if (Closeback && first_char == '}')
ind -= INCR;
point = orig;
return ind;
}
/* Look at the last two lines and guess at the correct indentation,
assuming C source (or similar). If we're not in this line's
indentation, though, or our new indentation matches the old,
just insert a tab.
*/
c_indent() on c_tab['\t']
{
int orig = point;
int orig_column = current_column();
to_indentation();
if (orig_column > current_column()) { /* if not in indentation */
point = orig;
insert('\t'); /* insert a tab */
} else if (prev_cmd == C_INDENT) /* repeated, make bigger */
to_column(orig_column + INCR);
else
to_column(c_compute_indent());
this_cmd = C_INDENT;
}
/*
Tell if more indent is required.
Assumes last line had no indent and point is at its start.
The hard case is distinguishing between these when Topindent is zero:
foo(); foo();
} }
func(bar) if (bar)
When point is just after these examples, we must examine the word
at "func" to distinguish the left example (the end of a function and
beginning of the next, requiring no indentation) from the right (the
end of a block and start of a conditional, requiring indentation).
*/
top_indent(prev_end, prev2_end)
{
int ret;
if (Topindent)
ret = prev_end == '{';
else if (ret = !index(";}", prev_end) && index(";{}", prev2_end))
if (prev_end != '{' && !c_statement_start())
ret = 0;
return ret;
}
/*
Tell if the last line begins a statement (rather than a function
declaration or the continuation of a previous statement).
Assumes point is at the start of the last line.
*/
c_statement_start()
{
return parse_string(1, "[{} \t]*(if|else|while|do|for)[^a-zA-Z0-9]",
(char *) 0);
}
just_open()
{
return Closeback && parse_string(1, "{[ \t]*\n", (char *) 0);
}
/* fix indentation if necessary when { is typed */
c_open() on c_tab['{']
{
int orig = point;
int orig_column; /* point's column */
int ind;
if (Closeback) {
orig_column = current_column();
to_indentation();
ind = current_column(); /* get line's indentation */
point = orig;
if (orig_column <= ind) /* if in indentation */
to_column(ind - INCR);
}
normal_character();
}
/* fix indentation if necessary when } is typed */
c_close() on c_tab['}']
{
if (point && check_single()) {
normal_character();
return;
} else {
normal_character();
c_close_compute();
}
}
c_close_compute()
{
int orig;
int orig_column,p_orig_column; /* point's column */
int quoflg,comflg,abspos;
int temp,ind;
char first,second;
char *left = "[{(", *right = "]})";
orig = point;
orig_column = current_column() - 1;
to_indentation();
ind = current_column();
if (ind < orig_column) {
point = orig;
return;
}
if (Matchdelim) {
point = orig;
match_character();
abspos = -1;
if (comflg = in_comment_check())
abspos = comment_position;
if (Match_position >= 0) {
point = p_orig_column = Match_position;
while (re_search(-1,"[]})\n]") && curchar() != '\n'
&& point > abspos)
if (comflg == in_comment()) {
point++;
second = character(point - 1);
first = left[index(right,second)-right];
if (!search_close(first,second)) {
say("Unmatched delimiter -- %c",second);
break;
} else
point = Match_position;
}
if (curchar() != '\n')
point = p_orig_column;
else
point++;
to_indentation();
ind = current_column();
point = orig - 1;
to_column(ind);
point++;
return;
}
} else if (Closeback) {
if (orig_column <= ind) /* if in indentation */
to_column(ind - INCR);
point = orig;
}
}
/* This routine implements indentation of switch statements.
When the ':' character is entered, it checks first to make sure
that the colon is not imbedded in some comment. Then, it makes
sure that it is not imbedded in a string. Finally, if the previous
line does not end in '{', the whole line is back indented on tab space.
Author: John Kubiatowicz
kubitron@athena.mit.edu
*/
switch_back() on c_tab[':']
{
int ind;
int orig;
normal_character();
orig = point;
point--;
if (!Switch_form || in_comment_check() ||
in_quote_check() || char_exists('?') || check_single()) {
point++;
return; /* Do nothing special */
}
to_indentation();
ind = current_column();
re_search(-1,C_SKIP);
if (point > 0 && index(":};",character(--point)))
ind -= INCR;
point = orig;
to_indentation();
to_column(ind);
to_end_line();
search(-1,":");
point++;
}
/* This checks to see if the point is within a comment. */
#define COM_SEARCH "(/%*)|(%*/)"
int in_comment_check()
{
comment_position = -1;
return in_comment();
}
int in_comment()
{
int orig = point;
if (!Comment_check_enable)
return 0;
if (comment_position != -1 && point > comment_position)
return comment_value;
quote_position = -1;
while (re_search(-1,COM_SEARCH) && curchar() == '/' && in_quotes());
comment_position = point; /* Point at which answer was derived */
comment_value = parse_string(1,"/*",(char *)0);
point = orig;
return comment_value;
}
/* This routine checks to see if the current character is imbedded
within a set of quotes. It returns 'TRUE' if the character follows
an odd number of double quotes on the current line. */
int in_quote_check()
{
quote_position = -1; /* Force full scan */
return in_quotes();
}
/* This routine checks the variable 'quote_position.' If it is not
equal to -1, and point is greater than it, it returns the value
'quote_value.' Otherwise, it scans backward from the current
point to see if there are an odd number of points between the
current point and the beginning of the line.
*/
int in_quotes()
{
int orig = point;
if (quote_position != -1 && point > quote_position)
return quote_value; /* Previous result still valid */
quote_position = -1;
quote_value = 0;
while ((--point) >= 0 && curchar() != '\n')
if (curchar() == '"' &&
!(point > 0 && ((character(point - 1) == '\'' &&
character(point + 1) == '\'') ||
escseq(point)))) {
quote_value = !quote_value; /* Toggle the quote flag */
if (quote_position == -1)
quote_position = point;
}
if (quote_position == -1)
quote_position = point;
point = orig;
return (quote_value);
}
/* Check to see if the character at 'check_point' is part of
an escape sequence.
*/
escseq(check_point)
char check_point;
{
int returnval = 0;
while (--check_point > 0 && character(check_point) == '\\')
returnval = !returnval;
return returnval;
}
/* Check to see if the character at 'point' could potentially
be enclosed in single quotes or part of an escape sequence ('\').
Note that an ending single quote is not required. This routine
is used as characters are being typed in, so presumably the final
quote doesn't exist yet.
*/
int check_single()
{
int backcount = 0;
int check_point = point;
if (!check_point)
return 0;
if (character(check_point - 1) == '\'' && !in_quotes()) {
if (check_point == 1)
return 1; /* Point of interest in quotes */
if (character(check_point - 2) == '\\' &&
(check_point == 2 || character(check_point - 3) != '\\'))
return 0; /* Single quote is part of escape sequence */
return (check_point == 2 ||
(character(check_point - 3) != '\'' &&
(check_point == 3 || character(check_point - 3) != '\\'
|| character(check_point - 4) != '\'')));
} else if (character(check_point - 1) == '\\')
return escseq(check_point);
else
return 0;
}
/* Check to see if the specified character exists earlier in the line. */
int char_exists(desired)
char desired;
{
int orig = point;
while ((--point) >= 0 && curchar() != '\n')
if (curchar() == desired) {
point = orig;
return 1; /* char exists */
}
point = orig;
return 0; /* Char does not exist */
}
/* Search back for matching closing delimiter */
int search_close(lchar,rchar)
char lchar,rchar;
{
char searchstr[20];
jmp_buf *old_level = top_level, this_level;
int orig = point;
int extradel = 0,quoflg,comflg,abspos;
Match_position = -1; /* No position to match */
top_level = &this_level;
if (setjmp(top_level)) {
top_level = old_level;
point = orig;
return 0;
}
if (rchar == ']')
strcpy(searchstr,"[][]");
else
sprintf(searchstr,"[%c%c]",lchar,rchar);
abspos = -1;
if (comflg = in_comment_check())
abspos = comment_position;
if (quoflg = in_quote_check() && quote_position > abspos)
abspos = quote_position;
while (re_search(-1,searchstr) && point > abspos)
if (comflg == in_comment() && quoflg == in_quotes()
&& !check_single())
if (curchar() == rchar)
extradel++;
else {
extradel--;
if (!extradel)
break;
}
top_level = old_level;
if (curchar() == lchar && !extradel) {
Match_position = point;
point = orig;
return 1;
} else {
point = orig;
return 0;
}
}
#define LEFTD "[({"
#define RIGHTD "])}"
/* This routine attempts to search for a matching delimiter,
then briefly shows it on the screen. */
command c_look_for_matching()
{
normal_character();
point--;
if (!check_single()) {
point++;
match_character();
} else
point++;
}
match_character()
{
char first,second;
char *left = LEFTD, *right = RIGHTD,*foo;
say("");
second = character(point - 1);
if (foo = index(right,second)) {
first = left[foo - right];
if (search_close(first,second))
show_match_line();
else
say("Unmatched delimiter");
}
}
/* Briefly display the window at Match_position, then return to point */
show_match_line()
{
int orig = point;
int time, oldstart = window_start;
point = Match_position;
maybe_refresh();
time = (window_start == oldstart) ? 5 : 10;
pause(time);
window_start = oldstart;
point = orig;
build_first = 1;
}
/* The following commands implement the extended mark functions.
Their names are basically self explanatory.
*/
command c_set_extended_mark()
{
int fnum = key - SET_MARK_BASE;
if (fnum < 0 || fnum > 9)
error("Bad key-- c_set_extended_mark");
if (extended_mark[fnum] == NULL)
extended_mark[fnum] = alloc_spot();
*(extended_mark[fnum]) = point; /* Get sticky mark */
say("Extended mark #%d set.",fnum + 1);
}
command c_get_extended_mark()
{
int fnum = key - GET_MARK_BASE;
if (fnum < 0 || fnum > 9)
error("Bad key-- c_get_extended_mark");
if (extended_mark[fnum] == NULL)
error("Extended mark #%d not set.",fnum+1);
else {
if (prev_ext_mark == NULL)
*(prev_ext_mark = alloc_spot()) = 0;
*prev_ext_mark = point;
point = *(extended_mark[fnum]);
build_first = 1;
say("Previous position saved.");
}
}
command c_retrieve_mark()
{
int temp = point;
if (prev_ext_mark == NULL)
error("No previous position.");
else {
point = *prev_ext_mark;
*prev_ext_mark = temp;
build_first = 1;
say("Previous position saved.");
}
}
/* Restore the current buffer from its associated disk file.
Note that two types of buffers cannot be reverted, process
buffers, and directory buffers. Note that the extended marks
are returned to their uninitialized states and the pointer
is placed at the beginning of the buffer.
*/
command reload_file() on cx_tab[CTRL('R')]
{
char answer[80];
int count;
if(!strcmp(bufname,"process"))
error("Process buffers cannot be reloaded!");
else if (!strcmp(bufname,"dired"))
error("Directory buffers cannot be reloaded!");
else {
get_string(answer, "Restore file from disk? [n]");
if (toupper(*answer) == 'Y') {
read_file(filename);
for (count = 0; count < 10; count++)
extended_mark[count] = NULL;
prev_ext_mark = NULL;
}
}
}